package thread;

import java.nio.channels.CancelledKeyException;
import java.nio.channels.SelectionKey;
import java.util.Objects;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cachedata.NioChannel;
import cachedata.NioSocketWrapper;
import enumerate.SocketEvent;
import enumerate.SocketState;
import handle.DealHandle;
import handle.Handle;
import util.ExceptionUtils;

class ProccessWorker implements Runnable {
	
	private static final Logger log = LoggerFactory.getLogger(ProccessWorker.class);

        private NioSocketWrapper socketWrapper;
        private SocketEvent event;
        
        private Handle handle =  new DealHandle();

        public ProccessWorker(NioSocketWrapper socketWrapper, SocketEvent event) {
            reset(socketWrapper, event);
        }


        public void reset(NioSocketWrapper socketWrapper, SocketEvent event) {
            Objects.requireNonNull(event);
            this.socketWrapper = socketWrapper;
            this.event = event;
        }


        @Override
        public final void run() {
            synchronized (socketWrapper) {
                // It is possible that processing may be triggered for read and
                // write at the same time. The sync above makes sure that processing
                // does not occur in parallel. The test below ensures that if the
                // first event to be processed results in the socket being closed,
                // the subsequent events are not processed.
                if (socketWrapper.isClosed()) {
                    return;
                }
                doRun();
            }
        }

        private void doRun() {
            NioChannel socket = socketWrapper.getSocket();
            SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
            try {
            	SocketState state = SocketState.OPEN;
                if (event == null) {
                    state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
                } else {
                    state = getHandler().process(socketWrapper, event);
                }
                if (state == SocketState.CLOSED) {
                    close(socket, key);
                }
            } catch (CancelledKeyException cx) {
                socket.getPoller().cancelledKey(key);
            } catch (VirtualMachineError vme) {
                ExceptionUtils.handleThrowable(vme);
            } catch (Throwable t) {
                log.error("", t);
                socket.getPoller().cancelledKey(key);
            } finally {
                socketWrapper = null;
                event = null;
                //return to cache
//               TODO processorCache.push(this);
            }
        }
        
        private Handle getHandler() {
			return handle;
		}


		private void close(NioChannel socket, SelectionKey key) {
            try {
                if (socket.getPoller().cancelledKey(key) != null) {
                    // SocketWrapper (attachment) was removed from the
                    // key - recycle the key. This can only happen once
                    // per attempted closure so it is used to determine
                    // whether or not to return the key to the cache.
                    // We do NOT want to do this more than once - see BZ
                    // 57340 / 57943.
//                  TODO  if (running) {
//                        if (!nioChannels.push(socket)) {
//                            socket.free();
//                        }
//                    }
                }
            } catch (Exception x) {
                log.error("",x);
            }
        }
}
